// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2025-01-09

package login

import (
	"context"
	"gitee.com/go-libs/perm"
	"gitee.com/go-libs/result"
	"gitee.com/go-libs/validator-iris/validator"
	"gitee.com/go-wares/framework-iris/framework/src/logics"
	"github.com/kataras/iris/v12"
	"gomq/app/authorizer"
	"gomq/app/errs"
	"gomq/app/models"
	// "gomq/app/perm"
	"gomq/app/services"
	"strings"
)

type Logic struct {
	request  *Request
	response *Response
}

func New() logics.Logical {
	return &Logic{
		request:  &Request{},
		response: &Response{},
	}
}

func (o *Logic) Before(_ context.Context, i iris.Context) (err error) {
	err = validator.Json(i, o.request)
	return
}

func (o *Logic) Run(ctx context.Context, i iris.Context) (res result.Result) {
	var (
		auth    *authorizer.Authentication
		err     error
		expire  int
		user    *models.User
		service = services.NewUserService()
		token   []byte
	)

	res = result.New()

	// Read user by username.
	if user, err = service.GetByUsername(ctx, o.request.Username); err != nil {
		res.WithError(errs.ErrDatabaseError)
		return
	}

	// Return error if user not found by username.
	if user == nil {
		res.WithError(errs.ErrUserNotFound)
		return
	}

	// Forbidden for disabled user.
	if user.Status.Disabled() {
		res.WithError(errs.ErrUserStatusDisabled)
		return
	}

	// Forbidden for locked user.
	if user.Status.Locked() {
		res.WithError(errs.ErrUserStatusLocked)
		return
	}

	// Compare request password with user.
	if user.Password != service.BuildPassword(o.request.Password, user.Salt) {
		res.WithError(errs.ErrUserPasswordNotMatch)
		return
	}

	// Generate authentication properties.
	auth = authorizer.NewAuthentication()
	auth.Id = user.Id
	auth.Name = user.Name
	auth.Username = user.Username
	auth.Roles = make([]perm.Role, 0)

	// Parse roles with user settings.
	for _, s := range strings.Split(user.Roles, `,`) {
		if s = strings.TrimSpace(s); s != "" {
			auth.Roles = append(auth.Roles, perm.Role(s))
		}
	}

	// Generate token.
	if token, expire, err = authorizer.New().Build(auth); err != nil {
		res.WithError(errs.ErrUserIssueToken)
		return
	}

	// Generate response fields
	o.response.Expire = expire
	o.response.Token = string(token)

	// Success issued.
	res.With(o.response)
	return
}
